TurboBus PCIe Bandwidth Pooling
瓶颈链路为何仍然空闲
GPU 显存不足时,系统会把暂时用不到的对象放到 Host Memory,需要时再通过 PCIe 搬回来。TurboBus 关注的对象包括三类:
- 按需模型加载:权重常驻 Host Memory,请求到来后逐层或整体载入 GPU。
- KV Cache 卸载推理:Prefill 后把 KV Cache 放到 Host Memory,Decode 时按需取回。
- 内存受限训练:把梯度和 Optimizer State 卸载到 CPU,更新后的权重再传回 GPU。
这是一笔明确的交换:用容量更大、价格更低的 Host Memory,换取额外的数据搬运。论文的 copy-free what-if 实验把 Copy 替换为空操作,用两次运行的差值估计不可忽略的传输成本。在其设置中,PipeSwitch 的 TTFT 变为 copy-free 的 1.7–3.0 倍,FlexGen 吞吐约为 copy-free 的十分之一,ZeRO-Offload 的迭代时间最多增加 24%。
真正促成 TurboBus 的,不只是“PCIe 很慢”,而是下面这个组合:
单条链路已经成为瓶颈,但服务器所有链路的总容量没有被同时用满。
论文用 NVML 观测 ZeRO-Offload 与 FlexGen:PCIe 3.0 x16 的标称容量为 16 GB/s,四组方向流量的平均利用率只有 8.39%–26.78%;超过 60% 的时间,链路带宽不高于 2 GB/s。空闲来自两个尺度:
- 作业内错峰:ZeRO-Offload 的不同 Rank 分别拥有不同层的 Optimizer State;反向传播逐层产生梯度,各 Rank 的 D2H 与 H2D 搬运并不同步。
- 作业间错峰:同机部署的计算密集、显存带宽密集或另一项 Offloading 作业,不会在相同时刻占满各自 PCIe 链路。
先看论文用来概括问题的第一张图。
图 1 左侧的黄色 GPU 受限于自己的 PCIe 链路,其余三条链路虽然空闲,却属于别的 GPU。右侧没有提高任何一条 PCIe 链路的速率,而是增加了访问 Host Memory 的路径:源 GPU 保留直连路径,同时通过 Scale-Up Fabric 把一部分数据交给邻居,再从邻居的 PCIe 链路进出 Host Memory。
这张图故意省略了最难的部分。跨作业时,源作业没有权限读写邻居 GPU;大对象不能完整缓存在 Relay GPU;H2D 与 D2H 可能争用同一条 NVLink;大权重还可能堵住更小的 KV Cache 请求。“能借路”只是物理机会,TurboBus 的系统贡献是把这次借用变成隔离、有限开销且可调度的公共服务。
哪些拓扑真的能借路
借路成立的关键,不是服务器上“有很多 GPU”,而是同时存在两类路径:
- GPU 到 Host Memory 的 PCIe 链路相对较慢,而且不同 GPU 能暴露出彼此独立的上行容量;
- GPU 之间的 Scale-Up Fabric 足够快,Relay Hop 不会代替 PCIe 成为新瓶颈。
图 4 展示了论文验证的三类机器。DGX V100 与 H800 先把 GPU 接入 NVSwitch;RTX 3090 机器则用点对点 NVLink。对 TurboBus 而言,两者都能提供一跳 Relay Path,但可借到的 PCIe 容量仍由底层 PCIe Switch 与 Host 侧拓扑决定。
以论文的 V100 测试床为例,GPU 间 NVLink 单方向带宽为 50 GB/s,PCIe 3.0 x16 为 16 GB/s。一次 Relay 的路径是:
1 | D2H:Source GPU --NVLink--> Relay GPU --PCIe--> Host Memory |
Relay Hop 比最终 PCIe Hop 快,因此可以被流水隐藏。双向并发的要求更严格:同一方向的 NVLink 最多同时承载两个 PCIe 速率的数据流,所以论文要求 Scale-Up 带宽至少约为 PCIe 的两倍,才能在不让 NVLink 成为瓶颈的前提下同时运行 H2D 与 D2H。
这给出了一个比“NVLink 比 PCIe 快”更完整的判据:可池化容量等于新路径真正暴露的独立慢速资源,而不是 Relay GPU 的数量。
从借路到系统服务
跨作业共享最先遇到的不是性能问题,而是所有权问题。普通进程不能访问另一个作业的 GPU 地址空间,也不应该因为借用一条 PCIe Link 获得这种权限。TurboBus 因此没有让客户端彼此连接,而是引入每节点一个特权 Daemon。
图 5 把论文的五个挑战映射为三个组件:
- Daemon Core 解决跨作业隔离(C1):它能看到节点内所有 GPU,客户端只能提交自己的 Tensor 与搬运请求,不能直接访问 Relay GPU。
- Executor 解决 Relay 开销与链路利用率(C2、C3):它把一次大传输切成 Block,沿直连和 Relay Path 流水执行。
- Scheduler 解决双向并发与请求公平性(C4、C5):它决定 H2D、D2H 何时共用 NVLink,也决定大小请求怎样交错。
控制面和数据面被明确分开。客户端与 Daemon 通过 UNIX Domain Socket 传递请求元数据;Payload 不经过 Socket,也不在 CPU 侧重复 Copy:
- Host Tensor 从 Linux
/dev/shm分配,Daemon 对同一共享内存区域执行mmap; - Device Tensor 由客户端导出 CUDA IPC Handle,Daemon 用
cudaIpcOpenMemHandle()打开同一段 GPU Memory; - Relay GPU 只物化当前正在流动的少量 Block,不缓存完整的模型权重或 KV Cache。
论文实现约 3,900 行代码,包括约 2,500 行 C++ Daemon、1,100 行 C++ Client Library 与 300 行 Python Frontend。Daemon 的 Listener Thread 用 Edge-Triggered epoll 接收请求,Worker Thread 运行 Scheduler 与 Executor;传输请求通过 cudaLaunchHostFunc 接入既有 CUDA Stream 依赖链。Daemon 不可用时,接口回退为源 GPU 自有链路上的 cudaMemcpy,牺牲池化加速但保留传输正确性。
这里的中心化设计有一个很现实的取舍:要跨作业调度,就需要一个同时看见所有路径与请求的控制点;但这个控制点也成为需要额外保护、监控和容错的特权服务。论文展示了进程隔离与直接 Copy 回退,没有给出恶意客户端、Daemon 重启恢复或生产级资源配额的完整评估。
块级流水如何填满链路
最朴素的 Relay 是 Store-and-Forward:先把整个对象从 Source 搬到 Relay GPU,再从 Relay 搬到 Host。这个方案同时有两个问题:多 GB 对象会占满 Relay 显存,NVLink 与 PCIe 两个阶段也只能串行工作。
TurboBus 把“调度一整个 Transfer”改成“调度固定大小的 Block”。这一步同时改变了 Buffer 生命周期、流水方式与路径分配粒度。
图 6 需要分三部分读。
直连与转发
图 6(a) 中,同一条 PCIe Link 对不同请求扮演不同角色。D2H 时,路径 1 是 GPU0 的直连,路径 2 先经 NVLink 到 GPU2,再借 GPU2 的 PCIe;H2D 时两者反过来。TurboBus 不把源 GPU 的直连当成特殊 Fast Path,而是把它与所有 Relay Path 一起交给 Allocator。
系统只做 Single-Hop Relay。多跳不会发现直接邻居之外的新 PCIe Link,只会叠加 NVLink 时延和调度复杂度。
双缓冲流水
图 6(b)(i) 表示不同大小的整次传输无法对齐,短传输结束后会留下 Pipeline Bubble。图 6(b)(ii) 把它们统一切成 Block:Relay GPU 一边通过 PCIe 送出当前 Block,一边通过 NVLink 接收下一个 Block。
在 V100 测试床上,Profiler 选择 16 MB Block;小于半个 Block、即 8 MB 的传输直接绕过 Relay Pipeline。每个方向需要两个 Buffer 实现一边填充、一边排空,H2D 与 D2H 合计四个 Buffer,因此 Relay 显存开销固定为:
$$
M_{relay}=2\ \text{directions}\times2\ \text{buffers}\times16\ \text{MB}=64\ \text{MB}.
$$
这里实际物化的是四个 Relay Buffer;完整 Payload 仍留在客户端 Host/Device Tensor 中,Block 只在流水经过 Relay GPU 时短暂存活。Block Size 也不是跨平台常数:论文在 RTX 3090 上仍选择 16 MB,在 H800 上选择 32 MB,原则是找到达到峰值吞吐的最小 Block,以平衡 Launch 开销、Buffer 占用和流水预热/排空时间。
图 6(b)(iii) 还展示了双向同步。作者最初让两个线程分别驱动 H2D 与 D2H,虽然二者使用独立 Copy Engine,吞吐仍比单方向下降最多 40%;论文推测原因是 CUDA Runtime 内部锁让一个 Stream 的同步影响了其他 Stream。最终实现用单一控制线程统一 Launch,在固定 Time Slot 边界同时同步所有活跃 Stream,避免独立 cudaStreamSynchronize 的相互干扰。
路径分配
图 6(c) 的 Transfer A 有 6 个 Block,Transfer B 有 2 个,共有 4 条路径。Transfer-Level 分配让 A 在 Stage 2 只占两条路径,另外两条空闲;B 仍要等到 Stage 3。Block-Level 分配则在每轮查看所有 Ready Block,直接用 B 的两个 Block 填满 A 留下的空位。
论文的微基准中,这个改动把吞吐提高最多 20%。它反映出一个比“把大对象切小”更重要的思想:只有调度粒度也缩小到 Block,固定大小流水才真正转化为全局链路利用率。
调度既要并发也要克制
把路径放进一个池后,请求之间开始互相影响。TurboBus 在两个维度采用不同策略。
H2D 与 D2H 之间,系统允许二者同时使用同一 NVLink。Single-Hop 约束保证一条有向 NVLink 最多承载两个 PCIe 速率的数据流;当 NVLink 带宽大于两倍 PCIe 时,两者并发仍由 PCIe 限速。图 6(b)(iii) 的统一 Time Slot 同步负责消除 Runtime 干扰。
同一方向的大小请求之间,系统利用 Offloading 请求大小呈双峰分布的特点:
- Chunking:大 Transfer 一次只允许一个 Chunk 进入 FCFS 队列,完成后再提交下一个。小请求最多被一个 Chunk 阻塞,不必等完整的大 Transfer。
- Token-Gated Admission:多个大 Transfer 若同时平分路径,会让所有请求一起变慢;Token 按到达顺序只允许一个大 Transfer 持续提交 Chunk。小请求不需要 Token,可以插入执行。
Chunking 把小请求的最坏等待从“一个完整大 Transfer”压到“一个 Chunk”,Token 则避免大请求互相拖慢。论文微基准显示,两种机制分别使测试请求的完成时间下降 15%–27%。这不是严格的全局最优调度,而是一组利用请求大小结构、实现成本较低的规则。
接入代码改变了什么
TurboBus 并不透明拦截任意 cudaMemcpy。应用要把参与池化的 Host/Device Tensor 交给它分配,并用 MemPair 发起传输。
图 7 左侧的 PyTorch 代码直接创建 CPU/GPU Tensor,再调用异步 copy_();右侧增加 tb.init() 与 tb.clean(),并做三处替换:
create_host_tensor()创建可被 Daemonmmap的 Host Tensor;create_dev_tensor()创建可通过 CUDA IPC 共享的 Device Tensor;pair(host, dev).to_dev()或to_host()向 Daemon 提交 H2D/D2H 请求。
因此,“最小改动”更准确地说,是无需重写模型计算图和控制流,但必须替换目标数据路径上的内存分配与 Copy API。如果既有框架在内部创建临时 Tensor、复用自定义 Allocator,或把 Copy 藏在 Fused Operator 中,接入成本会高于图 7 的独立示例。截至 2026 年 8 月 28 日,论文、作者发表列表和 SIGCOMM 项目页均未给出公开代码仓或 Artifact 链接,公开检索也未找到官方实现,因此本文无法独立验证 pip 安装、框架兼容范围和回退路径的实际行为。
实验证据支持到哪里
论文用一个 Amdahl 风格模型给收益划上限。设原始时间为:
$$
T=T_c+T_d,
$$
其中 $T_c$ 是计算及其他时间,$T_d$ 是不能被计算隐藏的传输时间;令 $f=T_d/T$,可用 PCIe Link 数为 $N$,理想池化后的上限为:
$$
T_{opt}=T_c+\frac{T_d}{N},\qquad
\text{Speedup}_{opt}=\frac{1}{1-f+f/N}.
$$
这个式子先给出一个重要边界:TurboBus 只能缩短 $T_d$,无法消除 $T_c$。FlexGen 的不可重叠传输占比约 90%,四条链路的理论上限可接近 3 倍;ZeRO-Offload 的传输占比约 24%,同样的四条链路也只有约 1.2 倍上限。
论文的主测试床是四张 32 GB V100、两个可池化 PCIe 3.0 x16 Link 的 DGX;RTX 3090 与八卡 H800 主要用于跨 PCIe 代际、Block Size 和扩展性的微基准。全部实验启用 NVIDIA MPS。端到端工作负载均使用 TP=1;论文认为 TP>1 的同步组内收益有限,机会主要存在于 TP Group、DP Replica 与共置作业之间。
| 场景 | 设置与对照 | TurboBus 结果 | 不能直接外推的部分 |
|---|---|---|---|
| 按需模型加载,Serverless Emulation | 4 个独立 GPU 请求流,Llama-3-8B,Poisson 到达 | P50 TTFT 降低 17%–25% | 相比 PipeSwitch+DeepPlan,P99 最多反而增加 10% |
| 按需模型加载,隔离环境 | Llama-3-8B、Llama-2-13B,1 条直连 + 1 条 Relay Link | TTFT 降低 22%–40%,距分析最优值不超过 5% | 是无外部干扰的两链路上限实验 |
| 两个 FlexGen 实例互相 Relay | OPT-6.7B、OPT-13B,90% KV Cache Offload | 80% 以上迭代优于 Vanilla,吞吐最高提高 50% | 其余迭代最多下降 3 tokens/s;不是每次都获益 |
| FlexGen 借共置作业链路 | 共置训练或推理作业 | FlexGen 吞吐最高 1.6 倍,捐出链路的作业开销小于 1% | 结论依赖共置作业没有把 PCIe/NVLink 放在关键路径上 |
| ZeRO-Offload 训练 | 4 卡 DP,GPT-J、Llama-2-7B、Phi-3-mini | 迭代时间降低 4%–7% | 只达到分析上限的 33%–41%,收益受较低传输占比限制 |
论文证明得最扎实的是:在它覆盖的三代 PCIe 与三类 Offloading 工作负载上,Relay 的固定开销足够小,池化确实能转化为端到端收益。但“最高 1.6 倍”不是所有 LLM Workload 的通用加速比;它来自特定 FlexGen Offload Policy 和共置组合。
我的判断与适用边界
我认为 TurboBus 最有价值的地方,不是又做了一套更快的 memcpy,而是提出了一种节点内 I/O 视角:Scale-Up Fabric 不只承载 Collective,也可以成为连接多个慢速出口的 Relay Backplane。只要系统满足“单资源受限、总体闲置、存在更快横向路径、所有权可安全重构”四个条件,原本静态绑定的链路就可能变成可调度资源。
这条思路真正落地时,限制同样清楚:
- Scale-Up Fabric 必须足够快:只有 PCIe Peer-to-Peer、没有 NVLink/NVSwitch 的机器会让 Relay 与原流量争用同一层 PCIe,收益消失。
- Host 侧路径必须有余量:H800 八链路实验在同一 NUMA 内扩展较好,跨到远端 NUMA 后因 UPI 与内存位置限制,八链路 H2D 反而从四链路的 3.6 倍降到约 3 倍。
- D2D 流量不能占满关键 NVLink:TurboBus 不调度 NCCL Collective;若 D2D 已经饱和或位于关键路径,Relay 会与它竞争,论文把这类联合调度留给未来工作。
- 集成式 CPU–GPU 不属于当前范围:Grace Hopper/Blackwell、MI300A 等平台用高带宽一致性互联替代 PCIe Host Path,TurboBus 的 PCIe Relay 不再解决主要瓶颈。
- 生产级隔离仍需补证据:论文展示了特权 Daemon、共享内存、CUDA IPC 与回退机制,但没有公开 Artifact,也没有报告恶意租户、Daemon 故障恢复、配额和审计方面的实验。
因此,判断一个平台是否值得引入 TurboBus,不应先问“有几张 GPU”,而应依次测量:每个作业不可隐藏的 H2D/D2H 占比、各 PCIe Link 的时间序列、GPU 间与 Host 侧路径带宽、NUMA 位置,以及 NCCL/D2D 是否已经占据 Scale-Up Fabric。只有这些账本同时出现可借容量,图 1 中那几条红色箭头才会成为真实收益。
总结
TurboBus 从一个利用率悖论出发:Offloading 作业被自己的 PCIe Link 卡住,节点内其他 Link 却因为阶段错峰和作业异构而空闲。它通过特权 Daemon 重构跨作业所有权,通过 Scale-Up Fabric 增加 Host Memory 路径,再用 Block Streaming、Block-Level Allocation、双向同步、Chunking 与 Token 把这些路径变成可用的共享带宽。
现在可以确认的是,这个设计在论文覆盖的 V100、RTX 3090、H800 微基准与三类端到端工作负载中获得了可测收益,并把 Relay 开销控制在接近分析上限的范围。尚不能确认的是,它在公开代码、复杂框架 Allocator、Scale-Up 与 NCCL 同时重载、跨 NUMA 大规模池化以及恶意多租户环境中的表现。
回到最初的问题,空闲带宽并不是真的“不可用”。它只是被静态所有权切碎了。TurboBus 的贡献,是用更快的横向 Fabric 和足够细的调度粒度,把这部分碎片重新拼成了一条数据路径。
参考资料
- Xinyu Yang, Kaiqiang Xu, Kai Chen. TurboBus: Pooling PCIe Bandwidth for LLM Workloads via Scale-Up Fabrics. ACM SIGCOMM 2026, DOI: 10.1145/3789240.3829130。本文方法、数值、Figure 1/4/5/6/7 与限制讨论均以该版本为准;论文按 CC BY 4.0 发布。
- ACM SIGCOMM. SIGCOMM 2026 Program Details。会议项目页确认论文、作者、会议信息与摘要结果。
- Kai Chen. Publications。作者主页列出的 SIGCOMM 2026 论文入口。
TurboBus PCIe Bandwidth Pooling